package resource

import (
	"fmt"
	"gitee.com/hexug/go-tools/common/validate"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/client-go/kubernetes"
)

var AppName = "resource"

type ListDeployRequest struct {
	Namespace          []string              `json:"namespaces" validate:"required"`
	ExcludeDeployNames map[string][]string   `json:"exclude_deploy_names" validate:"required"`
	Labels             []string              `json:"labels" validate:"required"` // key=value 类型
	ClientSet          *kubernetes.Clientset `json:"-"`
}

func NewListDeployRequest() *ListDeployRequest {
	return &ListDeployRequest{
		Namespace:          []string{},
		ExcludeDeployNames: make(map[string][]string),
		Labels:             []string{},
		ClientSet:          nil,
	}
}

func (l *ListDeployRequest) AddNamespaces(namespaces ...string) {
	l.Namespace = append(l.Namespace, namespaces...)
}

func (l *ListDeployRequest) AddExcludeDeployName(ns, deploy string) {
	if _, ok := l.ExcludeDeployNames[ns]; !ok {
		l.ExcludeDeployNames[ns] = []string{deploy}
	} else {
		l.ExcludeDeployNames[ns] = append(l.ExcludeDeployNames[ns], deploy)
	}
}

type ExcludeWorkloadCore struct {
	Namespace    string `json:"namespace"`
	Workload     string `json:"workload"`
	WorkloadType string `json:"workload_type"`
}

func NewDefaultExcludeWorkloadCore(namespace, workload, workloadType string) *ExcludeWorkloadCore {
	return &ExcludeWorkloadCore{
		Namespace:    namespace,
		Workload:     workload,
		WorkloadType: workloadType,
	}
}

type ListWorkloadRequest struct {
	Labels           []string               `json:"labels" validate:"required"` // key=value 类型
	Namespace        []string               `json:"namespace" validate:"required"`
	ClientSet        *kubernetes.Clientset  `json:"-"`
	ExcludeWorkloads []*ExcludeWorkloadCore `json:"exclude_workloads"`
	IncludeWorkloads []*ExcludeWorkloadCore `json:"include_workloads"`
}

func NewListWorkloadRequest() *ListWorkloadRequest {
	return &ListWorkloadRequest{
		Labels:           []string{},
		ExcludeWorkloads: []*ExcludeWorkloadCore{},
	}
}
func (l *ListWorkloadRequest) AddExcludeWorkload(workload *ExcludeWorkloadCore) {
	l.ExcludeWorkloads = append(l.ExcludeWorkloads, workload)
}
func (l *ListWorkloadRequest) AddIncludeWorkload(workload *ExcludeWorkloadCore) {
	l.IncludeWorkloads = append(l.IncludeWorkloads, workload)
}

func (l *ListWorkloadRequest) AddNamespaces(namespaces ...string) {
	l.Namespace = append(l.Namespace, namespaces...)
}
func (l *ListWorkloadRequest) AddLabels(label ...string) {
	l.Labels = append(l.Labels, label...)
}

func (l *ListWorkloadRequest) Validate() error {
	for _, workload := range l.ExcludeWorkloads {
		if err := validate.V().Struct(workload); err != nil {
			return err
		}
	}
	if l.ClientSet == nil {
		return fmt.Errorf("must specify clientSet")
	}
	return nil
}

type GatherWorkloadRequest struct {
	Namespaces []string               `json:"namespace"`
	ClientSet  *kubernetes.Clientset  `json:"-"`
	Workloads  []*ExcludeWorkloadCore `json:"workloads"`
}

func (g *GatherWorkloadRequest) Validate() error {
	if g.ClientSet == nil {
		return fmt.Errorf("must specify clientSet")
	}
	if len(g.Workloads) == 0 && len(g.Namespaces) == 0 {
		return fmt.Errorf("must specify workloads or namespace")
	}
	return nil
}

func NewGatherWorkloadRequest() *GatherWorkloadRequest {
	return &GatherWorkloadRequest{
		Workloads:  []*ExcludeWorkloadCore{},
		Namespaces: []string{},
	}
}
func (g *GatherWorkloadRequest) AddWorkload(workload *ExcludeWorkloadCore) {
	g.Workloads = append(g.Workloads, workload)
}
func (g *GatherWorkloadRequest) AddNamespaces(namespaces ...string) {
	g.Namespaces = append(g.Namespaces, namespaces...)
}

type WorkloadCore struct {
	*ExcludeWorkloadCore
	Workload interface{} `json:"workload" validate:"required"`
}

func NewWorkloadCore(namespace, workload, workloadType string) *WorkloadCore {
	return &WorkloadCore{
		ExcludeWorkloadCore: NewDefaultExcludeWorkloadCore(namespace, workload, workloadType),
	}
}

type ListWorkloadRespone struct {
	Items map[string]*WorkloadCore `json:"items"`
}

func NewListWorkloadRespone() *ListWorkloadRespone {
	return &ListWorkloadRespone{
		Items: make(map[string]*WorkloadCore),
	}
}
func (l *ListWorkloadRespone) AddWorkload(key string, workload *WorkloadCore) {
	l.Items[key] = workload
}

type ServiceCore struct {
	Namespace    string            `json:"namespace" validate:"required"`
	WorkloadType string            `json:"workload_type" validate:"required"`
	Workload     string            `json:"workload" validate:"required"`
	Selectors    []string          `json:"selectors" validate:"required"`
	SelectorMap  map[string]string `json:"selector_map"`
}

func NewDefaultServiceCore() *ServiceCore {
	return &ServiceCore{
		Selectors:   []string{},
		SelectorMap: make(map[string]string),
	}
}
func NewServiceCore(namespace, workload, workloadType string) *ServiceCore {
	return &ServiceCore{
		Namespace:    namespace,
		Workload:     workload,
		WorkloadType: workloadType,
		Selectors:    []string{},
		SelectorMap:  make(map[string]string),
	}
}

func (s *ServiceCore) AddSelectors(selector ...string) {
	s.Selectors = append(s.Selectors, selector...)
}
func (s *ServiceCore) FromDeployAddSelectors(dep *appsv1.Deployment) {
	if s.SelectorMap == nil {
		s.SelectorMap = make(map[string]string)
	}
	for k, v := range dep.Spec.Selector.MatchLabels {
		s.AddSelectors(fmt.Sprintf("%s=%s", k, v))
		s.SelectorMap[k] = v
	}
}

type ListServiceRequest struct {
	Services  []*ServiceCore        `json:"services" validate:"required"`
	ClientSet *kubernetes.Clientset `json:"clientSet" validate:"required"`
}

func NewListServiceRequest() *ListServiceRequest {
	return &ListServiceRequest{
		Services: []*ServiceCore{},
	}
}

func (l *ListServiceRequest) AddServices(services ...*ServiceCore) {
	l.Services = append(l.Services, services...)
}

func (l *ListServiceRequest) Validate() error {
	for _, service := range l.Services {
		if err := validate.V().Struct(service); err != nil {
			return err
		}
	}
	if l.ClientSet == nil {
		return fmt.Errorf("must specify clientSet")
	}
	return nil
}

type ListServiceRespone struct {
	Namespace    string          `json:"namespace" validate:"required"`
	Workload     string          `json:"workload" validate:"required"`
	WorkloadType string          `json:"workload_type" validate:"required"`
	Services     *corev1.Service `json:"services" validate:"required"`
}

func NewListServiceRespone() *ListServiceRespone {
	return &ListServiceRespone{}
}

type ConfigMapCore struct {
	Namespace    string   `json:"namespace" validate:"required"`
	Names        []string `json:"names"`
	WorkloadType string   `json:"workload_type" validate:"required"`
	Workload     string   `json:"workload" validate:"required"`
}

func NewConfigMapCore(namespace, workloadType, workload string, names ...string) *ConfigMapCore {
	return &ConfigMapCore{
		Namespace:    namespace,
		Names:        names,
		WorkloadType: workloadType,
		Workload:     workload,
	}
}

type ListConfigMapRequest struct {
	ConfigMaps []*ConfigMapCore      `json:"config_maps" validate:"required"`
	ClientSet  *kubernetes.Clientset `json:"clientSet" validate:"required"`
}

func NewListConfigMapRequest() *ListConfigMapRequest {
	return &ListConfigMapRequest{
		ConfigMaps: []*ConfigMapCore{},
	}
}
func (l *ListConfigMapRequest) AddConfigMapCores(configMaps ...*ConfigMapCore) {
	l.ConfigMaps = append(l.ConfigMaps, configMaps...)
}
func (l *ListConfigMapRequest) AddConfigMap(namespace, workloadType, workload string, names ...string) {
	l.AddConfigMapCores(NewConfigMapCore(namespace, workloadType, workload, names...))
}
func (l *ListConfigMapRequest) Validate() error {
	for _, configMap := range l.ConfigMaps {
		if err := validate.V().Struct(configMap); err != nil {
			return err
		}
	}
	if l.ClientSet == nil {
		return fmt.Errorf("must specify clientSet")
	}
	return nil
}

type ListConfigMapResponse struct {
	*ConfigMapCore
	ConfigMaps []*corev1.ConfigMap
}

func NewListConfigMapResponse(req *ConfigMapCore) *ListConfigMapResponse {
	return &ListConfigMapResponse{
		ConfigMapCore: req,
	}
}
func (l *ListConfigMapResponse) SetConfigMap(configMap *corev1.ConfigMap) {
	l.ConfigMaps = append(l.ConfigMaps, configMap)
}

// RBACInfo 保存结果
type RBACInfo struct {
	ServiceAccount *corev1.ServiceAccount
	RoleBindings   []*rbacv1.RoleBinding
	ClusterRBs     []*rbacv1.ClusterRoleBinding
	Roles          []*rbacv1.Role
	ClusterRoles   []*rbacv1.ClusterRole
}

func NewRBACInfo() *RBACInfo {
	return &RBACInfo{
		ServiceAccount: nil,
		RoleBindings:   []*rbacv1.RoleBinding{},
		ClusterRBs:     []*rbacv1.ClusterRoleBinding{},
		Roles:          []*rbacv1.Role{},
		ClusterRoles:   []*rbacv1.ClusterRole{},
	}
}
func (l *RBACInfo) AddRoleBinding(rb ...*rbacv1.RoleBinding) {
	l.RoleBindings = append(l.RoleBindings, rb...)
}

func (l *RBACInfo) AddClusterRoleBinding(crb ...*rbacv1.ClusterRoleBinding) {
	l.ClusterRBs = append(l.ClusterRBs, crb...)
}
func (l *RBACInfo) AddRole(role ...*rbacv1.Role) {
	l.Roles = append(l.Roles, role...)
}
func (l *RBACInfo) AddClusterRole(cr ...*rbacv1.ClusterRole) {
	l.ClusterRoles = append(l.ClusterRoles, cr...)
}

type FromWorkloadGetRBACResponse struct {
	Items map[string]*RBACInfo `json:"items"`
}

func NewFromWorkloadGetRBACResponse() *FromWorkloadGetRBACResponse {
	return &FromWorkloadGetRBACResponse{
		Items: make(map[string]*RBACInfo),
	}
}
func (l *FromWorkloadGetRBACResponse) AddRBACInfo(key string, info *RBACInfo) {
	l.Items[key] = info
}

type FromWorkloadGetResourceRequest struct {
	Workloads []interface{}         `json:"workloads"` // 存放各种 workload 对象
	ClientSet *kubernetes.Clientset `json:"clientSet" validate:"required"`
}

func NewFromWorkloadGetResourceRequest() *FromWorkloadGetResourceRequest {
	return &FromWorkloadGetResourceRequest{
		Workloads: []interface{}{},
	}
}
func (l *FromWorkloadGetResourceRequest) AddWorkload(workload ...interface{}) {
	l.Workloads = append(l.Workloads, workload...)
}

func (l *FromWorkloadGetResourceRequest) Validate() error {
	if l.ClientSet == nil {
		return fmt.Errorf("must specify clientSet")
	}
	return nil
}

type WorkloadVolumes struct {
	PVCs []*corev1.PersistentVolumeClaim
	PVs  []*corev1.PersistentVolume
}

func NewWorkloadVolumes() *WorkloadVolumes {
	return &WorkloadVolumes{
		PVCs: []*corev1.PersistentVolumeClaim{},
		PVs:  []*corev1.PersistentVolume{},
	}
}
func (l *WorkloadVolumes) AddPVC(pvc *corev1.PersistentVolumeClaim) {
	l.PVCs = append(l.PVCs, pvc)
}
func (l *WorkloadVolumes) AddPV(pv *corev1.PersistentVolume) {
	l.PVs = append(l.PVs, pv)
}

type FromWorkloadGetPVPVCResponse struct {
	Items map[string]*WorkloadVolumes
}

func NewFromWorkloadGetPVPVCResponse() *FromWorkloadGetPVPVCResponse {
	return &FromWorkloadGetPVPVCResponse{
		Items: make(map[string]*WorkloadVolumes),
	}
}
func (l *FromWorkloadGetPVPVCResponse) AddWorkloadVolumes(key string, volumes *WorkloadVolumes) {
	l.Items[key] = volumes
}

type SecretCore struct {
	Namespace string           `json:"namespace"`
	Secrets   []*corev1.Secret `json:"secrets"`
}

func NewSecretCore(ns string) *SecretCore {
	return &SecretCore{
		Namespace: ns,
		Secrets:   []*corev1.Secret{},
	}
}
func (l *SecretCore) AddSecret(secret *corev1.Secret) {
	l.Secrets = append(l.Secrets, secret)
}

type FromWorkloadGetSecretResponse struct {
	Items map[string]*SecretCore `json:"items"`
}

func NewFromWorkloadGetSecretResponse() *FromWorkloadGetSecretResponse {
	return &FromWorkloadGetSecretResponse{
		Items: make(map[string]*SecretCore),
	}
}
func (l *FromWorkloadGetSecretResponse) AddSecretCore(key string, core *SecretCore) {
	l.Items[key] = core
}

type CheckNamespaceRequest struct {
	Namespaces []string              `json:"namespaces"`
	ClientSet  *kubernetes.Clientset `json:"-"`
}

func (l *CheckNamespaceRequest) Validate() error {
	if len(l.Namespaces) == 0 {
		return fmt.Errorf("must specify namespaces")
	}
	if l.ClientSet == nil {
		return fmt.Errorf("must specify clientSet")
	}
	return nil
}

func NewCheckNamespaceRequest() *CheckNamespaceRequest {
	return &CheckNamespaceRequest{
		Namespaces: []string{},
	}
}
func (l *CheckNamespaceRequest) AddNamespace(namespaces ...string) {
	l.Namespaces = append(l.Namespaces, namespaces...)
}

type CheckNamespaceResponse struct {
	Namespaces []string `json:"namespaces"`
}

func NewCheckNamespaceResponse() *CheckNamespaceResponse {
	return &CheckNamespaceResponse{
		Namespaces: []string{},
	}
}

func (l *CheckNamespaceResponse) AddNamespace(namespaces ...string) {
	l.Namespaces = append(l.Namespaces, namespaces...)
}

type ResourceCore struct {
	Key           string `json:"key" validate:"required"` // 传递的关键key
	Namespace     string `json:"namespace"`
	ResourcesKind string `json:"resources_kind" validate:"required"`
	Version       string `json:"version" validate:"required"`
	Name          string `json:"name" validate:"required"`
	Resource      any    `json:"resource" validate:"required"`
}

func NewDefaultResourceCore(key string) *ResourceCore {
	return &ResourceCore{
		Key: key,
	}
}
func (r *ResourceCore) SetNamespace(ns string) {
	r.Namespace = ns
}

func (r *ResourceCore) SetResourcesKind(kind string) {
	r.ResourcesKind = kind
}
func (r *ResourceCore) SetVersion(version string) {
	r.Version = version
}
func (r *ResourceCore) SetName(name string) {
	r.Name = name
}
func (r *ResourceCore) SetResource(resource any) {
	r.Resource = resource
}

func NewResourceCore(key, namespace, resourcesKind, version, name string, resource any) *ResourceCore {
	return &ResourceCore{
		Key:           key,
		Namespace:     namespace,
		ResourcesKind: resourcesKind,
		Version:       version,
		Name:          name,
		Resource:      resource,
	}
}

type CreateResourceRequest struct {
	ClientSet *kubernetes.Clientset `json:"clientSet" validate:"required"`
	Items     []*ResourceCore       `json:"items"`
}

func (l *CreateResourceRequest) Validate() error {
	if l.ClientSet == nil {
		return fmt.Errorf("must specify clientSet")
	}
	for _, item := range l.Items {
		if err := validate.V().Struct(item); err != nil {
			return err
		}
	}
	return nil
}

func NewCreateResourceRequest() *CreateResourceRequest {
	return &CreateResourceRequest{
		Items: []*ResourceCore{},
	}
}
func (l *CreateResourceRequest) AddResource(key, namespace, resourcesKind, version, name string, resource any) {
	l.Items = append(l.Items, NewResourceCore(key, namespace, resourcesKind, version, name, resource))
}
